package com.yomahub.liteflow.example.utils;

import com.jcraft.jsch.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.Properties;
import java.util.UUID;
import java.util.Vector;
import java.util.function.Consumer;

/**
 * Created by joe强 on 2018/10/23 14:58
 */
@Data
@Slf4j
@Component
@ConfigurationProperties(prefix = "ssh-server-config")
public class SshServerUtils {
    private static final Logger lg = LoggerFactory.getLogger(SshServerUtils.class);
    private static Session session;

    private String host;
    private String username;
    private String passwd;
    private Integer port;

    public Session getSession() {
        Session session = SshServerUtils.connect(
                username, passwd, host, port
        );
        return session;
    }

    public void tailLog(ChannelExec channelExec, Consumer<String> consumer) {
        channelExec.setCommand("tail -f /export/server/spark/logs/appender-log.log");
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(channelExec.getInputStream()));
            channelExec.connect();
            String line;
            while (true) {
                line = reader.readLine();
                if (line == null) {
                    break;
                } else {
                    // 处理读取到的日志信息
                    consumer.accept(line);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (JSchException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }


    }

    private void clearls(ChannelSftp channelSftp, String dstPath) throws SftpException {

        String dirPath = getDirPath(dstPath);

        Vector dirFileList = channelSftp.ls("/home/shhadoop/server-pyspark/qar");
        Boolean tag = false;
        for (Object file : dirFileList) {
            ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) file;
            String filename = entry.getFilename();
            if (filename.equals("P737M06")) {
                tag = true;
                break;
            }
        }

        if(!tag) {
            channelSftp.mkdir("/home/shhadoop/server-pyspark/qar/P737M06");

        }



        String filename = getFilename(dstPath);
        Vector fileList = channelSftp.ls(dirPath);
        // 检查文件是否存在
        for (Object obj : fileList) {
            ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) obj;
            if (entry.getFilename().equals(filename)) {
                Vector ls = channelSftp.ls(dstPath);
                ls.clear();
                break;
            }
        }
    }

    private String getPreffux(String path) {
        String name = new File(path).getName();
        return name.substring(0, name.lastIndexOf("."));
    }

    private String getSuffux(String path) {
        String name = new File(path).getName();
        return name.substring(name.lastIndexOf("."), name.length());
    }

    private String getFilename(String path) {
        return new File(path).getName();
    }

    private String getDirPath(String path) {
        String name = new File(path).getName();
        return path.substring(0, path.lastIndexOf(name) - 1);
    }

    /**
     * @param executeContent      执行文件字符串
     * @param executeDstPath      远程生成文件路径
     * @param templateContent     方法文件字符串
     * @param dstPath             远程生成文件路径
     * @param channelExecConsumer 执行脚本
     * @param consumer            打印脚本执行日志
     */
    public void executeSsh(String executeContent, String executeDstPath, String templateContent, String dstPath, Consumer<ChannelExec> channelExecConsumer, Consumer<String> consumer) {
        ByteArrayInputStream executeContentByteStream = new ByteArrayInputStream(executeContent.getBytes());
        BufferedInputStream executeContentInputStream = new BufferedInputStream(executeContentByteStream);

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(templateContent.getBytes());
        BufferedInputStream bufferedInputStream = new BufferedInputStream(byteArrayInputStream);
        Session session = SshServerUtils.connect(
                username, passwd, host, port
        );
        ChannelSftp channelSftp = null;
        ChannelExec channelExec = null;
        try {
            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();
            // 创建远程文件,先删除，在创建
            clearls(channelSftp, dstPath);
            channelSftp.put(bufferedInputStream, dstPath, 0);
            clearls(channelSftp, executeDstPath);
            channelSftp.put(executeContentInputStream, executeDstPath, 0);
            channelSftp.disconnect();
            channelExec = (ChannelExec) session.openChannel("exec");
            // 异步执行执行脚本启动spark
            channelExecConsumer.accept(channelExec);
            channelExec.setErrStream(System.err);
            channelExec.connect();
            // 异步执行执行脚本打印日志
            tailLog(channelExec, consumer);
        } catch (JSchException e) {
            throw new RuntimeException(e);
        } catch (SftpException e) {
            throw new RuntimeException(e);
        } finally {
            channelExec.disconnect();
            channelSftp.disconnect();
            session.disconnect();
        }
    }

    /**
     * 创建多级目录，忽略文件
     * sshServerUtils.mkdir("/home/ftpuser/","/home/ftpuser/aa/bb/a.txt");
     * @param rootPath
     * @param path
     */
    public void mkdir(String path){
        Session session = SshServerUtils.connect(
                username, passwd, host, port
        );
        ChannelSftp channelSftp = null;
        try {
            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();
            mkdirPath(channelSftp, path);
            channelSftp.disconnect();
        } catch (JSchException e) {
            throw new RuntimeException(e);
        } finally {
            session.disconnect();
        }
    }
    /**
     * 创建所有的目录
     * @param channelSftp
     * @param path
     */
    private void mkdirPath(ChannelSftp channelSftp, String path) {
        File file = new File(path);
        String name = file.getName();
        String substring = path.substring(0, path.lastIndexOf(name));
        String[] split = substring.split("/");
        String splitPath = "";
        for (String s : split) {
            if (s.isEmpty()) {
                continue;
            }
            splitPath = splitPath + "/" + s;
            try {
                channelSftp.ls(splitPath);
            } catch (SftpException e) {
                if (e.toString().equals("2: No such file")) {
                    try {
                        channelSftp.mkdir(splitPath);
                    } catch (SftpException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
        }
    }
    /**
     * 将指定的字符串上传到远程服务器，并支持ssh脚本
     *
     * @param templateContent 生成文件的字符串
     * @param dstPath         远程文件路径
     * @param consumer        ssh脚本执行钩子
     * @param consumer        taillog日志跟踪输出
     * @return
     */
    public void executeSsh(String templateContent, String dstPath, Consumer<ChannelExec> channelExecConsumer, Consumer<String> consumer) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(templateContent.getBytes());
        BufferedInputStream bufferedInputStream = new BufferedInputStream(byteArrayInputStream);
        Session session = SshServerUtils.connect(
                username, passwd, host, port
        );
        ChannelSftp channelSftp = null;
        ChannelExec channelExec = null;
        try {
            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();
            // 创建远程文件,先删除，在创建
            clearls(channelSftp, dstPath);
            channelSftp.put(bufferedInputStream, dstPath, 0);
            channelSftp.disconnect();
            channelExec = (ChannelExec) session.openChannel("exec");
            // 异步执行执行脚本启动spark
            channelExecConsumer.accept(channelExec);
            channelExec.setErrStream(System.err);
            channelExec.connect();
            // 异步执行执行脚本打印日志
            tailLog(channelExec, consumer);
        } catch (JSchException e) {
            throw new RuntimeException(e);
        } catch (SftpException e) {
            throw new RuntimeException(e);
        } finally {
            channelExec.disconnect();
            channelSftp.disconnect();
            session.disconnect();
        }
    }


    // 连接服务器
    public static Session connect(String username, String passwd, String host, int port) {
        try {
            JSch jsch = new JSch();
            // 获取sshSession
            session = jsch.getSession(username, host, port);
            // 添加密码
            session.setPassword(passwd);
            Properties sshConfig = new Properties();
            // 严格主机密钥检查
            sshConfig.put("StrictHostKeyChecking", "no");
            session.setConfig(sshConfig);
            // 开启sshSession连接
            session.connect();
            lg.info("Server connection successful.");
            return session;
        } catch (JSchException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 执行相关命令-----生成文件并输出
    public static String execCmd(String command, String username, String passwd, String host, int port, String outFilePath) {

        String resultJson = null;
        ChannelExec channelExec = null;
        if (command != null) {
            try {
                connect(username, passwd, host, port);
                channelExec = (ChannelExec) session.openChannel("exec");
                // 设置需要执行的shell命令
                channelExec.setCommand(command);
                lg.info("linux命令:" + command);
                channelExec.setInputStream(null);
                channelExec.setErrStream(System.err);
                channelExec.connect();
                // 读数据--执行结果到outFilePath,读取文件流返回来、
                resultJson = getServerData(host, port, username, passwd, outFilePath);
            } catch (JSchException e) {
                e.printStackTrace();
            } finally {
                if (null != channelExec) {
                    channelExec.disconnect();
                }
            }
        }
        return resultJson;
    }

    // 从 服务器 获取 数据
    private static String getServerData(String host, int port, String username,
                                        String password, String filePath) {

        ChannelSftp sftp = null;
        StringBuffer buffer = new StringBuffer();
        try {

            if (!session.isConnected()) {
                connect(username, password, host, port);
            }

            // 获取sftp通道
            Channel channel = session.openChannel("sftp");
            // 开启
            channel.connect();
            sftp = (ChannelSftp) channel;
            lg.info("Connected to " + host + ".");
            // 获取生成文件流
            InputStream inputStream = sftp.get(filePath);
            BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
            String line = "";
            while ((line = in.readLine()) != null) {
                buffer.append(line);
            }
            // 关闭流
            inputStream.close();
            in.close();

            lg.info(" 执行结果为: " + buffer.toString());

        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSchException e) {
            e.printStackTrace();
        } catch (SftpException e) {
            e.printStackTrace();
        } finally {
            if (null != sftp) {
                sftp.quit();
            }
            closeSession();
        }
        return buffer.toString();
    }


    // 直接输出结果
    public static String execCmd(String command, String username, String passwd, String host, int port) {

        String resultJson = null;
        ChannelExec channelExec = null;
        if (command != null) {
            try {
                connect(username, passwd, host, port);
                channelExec = (ChannelExec) session.openChannel("exec");
                // 设置需要执行的shell命令
                channelExec.setCommand(command);
                lg.info("linux命令:" + command);
                channelExec.setErrStream(System.err);
                channelExec.connect();
                // 读数据
                InputStream in = channelExec.getInputStream();
                resultJson = IOUtils.toString(in, "UTF-8");
                // 关闭流
                in.close();
                lg.info(" 执行结果为: " + resultJson);
                return resultJson;
            } catch (JSchException e) {
                e.printStackTrace();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                if (null != channelExec) {
                    channelExec.disconnect();
                }
            }
        }
        return resultJson;
    }

    public static void closeSession() {
        if (session != null) {
            session.disconnect();
        }

    }

    public static String getUUID() {
        UUID uuid = UUID.randomUUID();
        String str = uuid.toString();
        return str.replace("-", "");
    }
}